#include <linux/types.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/delay.h>
#include <mach/io.h>
#include <linux/io.h>
#include <mach/fh_predefined.h>
#include "chip.h"
#include "../soc.h"

static DEFINE_SPINLOCK(scu_lock);

void fh_pmu_set_reg(unsigned int offset, unsigned int data)
{
	unsigned long flag;

	spin_lock_irqsave(&scu_lock, flag);
	writel(data, (void __iomem *)VA_SYS_REG_BASE + offset);
	spin_unlock_irqrestore(&scu_lock, flag);
}
EXPORT_SYMBOL(fh_pmu_set_reg);

unsigned int fh_pmu_get_reg(unsigned int offset)
{
	return readl((void __iomem *)VA_SYS_REG_BASE + offset);
}
EXPORT_SYMBOL(fh_pmu_get_reg);

void fh_pmu_set_reg_m(unsigned int offset, unsigned int data, unsigned int mask)
{
	fh_pmu_set_reg(offset, (fh_pmu_get_reg(offset) & (~(mask))) |
				       ((data) & (mask)));
}
EXPORT_SYMBOL(fh_pmu_set_reg_m);

unsigned long long fh_get_pts64(void)
{
	unsigned long lo, hi;

	fh_pmu_set_reg(REG_PTS_UPDATE, 0);
	hi = fh_pmu_get_reg(REG_PTS_CTRL1);
	lo = fh_pmu_get_reg(REG_PTS_CTRL2);
	return ((u64)lo|((u64)hi)<<32);
}
EXPORT_SYMBOL(fh_get_pts64);

void fh_get_chipid(unsigned int *plat_id, unsigned int *chip_id)
{
	unsigned int _plat_id = 0;

	_plat_id = fh_pmu_get_reg(REG_PMU_CHIP_ID);
	if (plat_id != NULL)
		*plat_id = _plat_id;

	if (chip_id != NULL)
		*chip_id = 0;
}
EXPORT_SYMBOL(fh_get_chipid);

unsigned int fh_pmu_get_ddrsize(void)
{
	return fh_pmu_get_reg(REG_PMU_DDR_SIZE);
}
EXPORT_SYMBOL_GPL(fh_pmu_get_ddrsize);

void fh_pmu_perfmon_init(int timelow, int timehigh)
{
	/* enable perfmon0~5 clk, busmon timer clk */
	fh_pmu_set_reg_m(REG_DMC_CLK_CTRL, (0x001000fe), (0x001000fe));
	fh_pmu_set_reg_m(REG_DFIMON_TMR1_CTRL0, (0x3), (0x3));
	fh_pmu_set_reg_m(REG_DFIMON_TMR1_CTRL0, (0x1), (0x3));

	//high time
	fh_pmu_set_reg(REG_HIGH_LEN_TIMER1, timehigh);
	//low time
	fh_pmu_set_reg(REG_LOW_LEN_TIMER1, timelow);
	/* perf_trigger_sel */
	fh_pmu_set_reg_m(REG_PERF_TRIGGER, 0x2, 0x2);
}
EXPORT_SYMBOL(fh_pmu_perfmon_init);

void fh_pmu_perfmon_timer_cnt_clr(void)
{
	fh_pmu_set_reg_m(REG_DFIMON_TMR1_CTRL0, 0x0, 0x3);
	fh_pmu_set_reg_m(REG_DFIMON_TMR1_CTRL0, 0x1, 0x3);
}
EXPORT_SYMBOL(fh_pmu_perfmon_timer_cnt_clr);

void fh_pmu_perfmon_rd_trigger(void)
{
	/* perf_rd_trigger */
	fh_pmu_set_reg_m(REG_PERF_TRIGGER, 0x1, 0x1);
}
EXPORT_SYMBOL(fh_pmu_perfmon_rd_trigger);

void fh_pmu_set_mclk(int freq)
{
	fh_pmu_set_reg_m(REG_CKG_I2S0_CTL, 0x2, 0x3); // i2s_clk=648M
	fh_pmu_set_reg(REG_IIS0_CLK_CTRL, 0);
	fh_pmu_set_reg(REG_CKG_I2S_FRAC_DIV_N, I2S_CLK_FREQ/1600);
	fh_pmu_set_reg(REG_CKG_I2S_FRAC_DIV_M, freq/800);

	fh_pmu_set_reg(REG_IIS0_CLK_CTRL, CKG_I2S_FRAC_DIV_EN|CKG_I2S_FAST_EN);
}
EXPORT_SYMBOL(fh_pmu_set_mclk);

static void _reset_ip(int offset, int bit)
{
	fh_pmu_set_reg_m(offset, BIT(bit), BIT(bit));
	udelay(1);
	fh_pmu_set_reg_m(offset, 0, BIT(bit));
}

static void _reset_ip_wc(int offset, int bit)
{
	fh_pmu_set_reg_m(offset, BIT(bit), BIT(bit));
	while (fh_pmu_get_reg(offset) & BIT(bit))
		;
}

void fh_pmu_reset_i2s(void)
{
	_reset_ip(REG_AP_PERI_SOFT_RST0, I2S0_SOFT_RST);
}
EXPORT_SYMBOL(fh_pmu_reset_i2s);

void fh_pmu_set_dma_sel_low(unsigned int val)
{
	fh_pmu_set_reg(REG_DMA_SEL_LOW, val);
}
EXPORT_SYMBOL(fh_pmu_set_dma_sel_low);

void fh_pmu_set_dma_sel_high(unsigned int val)
{
	fh_pmu_set_reg(REG_DMA_SEL_HIGH, val);
}
EXPORT_SYMBOL(fh_pmu_set_dma_sel_high);

void fh_pmu_set_dma0_axcache(unsigned int val)
{
	fh_pmu_set_reg(REG_DMA0_AXCACHE, val);
}
EXPORT_SYMBOL(fh_pmu_set_dma0_axcache);

void fh_pmu_set_dma1_axcache(unsigned int val)
{
	fh_pmu_set_reg(REG_DMA1_AXCACHE, val);
}
EXPORT_SYMBOL(fh_pmu_set_dma1_axcache);

void mol_pmu_set_efuse_lock_blk(unsigned int val)
{
	fh_pmu_set_reg(REG_EFUSE_CTRL_16, val);
}
EXPORT_SYMBOL(mol_pmu_set_efuse_lock_blk);

void fh_pmu_spi_slave_enable(unsigned int enable)
{
	fh_pmu_set_reg(REG_SPI0_MODE, enable);
}
EXPORT_SYMBOL(fh_pmu_spi_slave_enable);

void fh_pmu_reset_rsa(void)
{
	_reset_ip(REG_AP_PERI_SOFT_RST0, RSA_SOFT_RST);
}
EXPORT_SYMBOL(fh_pmu_reset_rsa);

void fh_pmu_usb_axi_clk_en(unsigned int is_u3, unsigned int enable)
{
	if (is_u3 == 0) {
		/* 0x2d would be better */
		fh_pmu_set_reg_m(REG_USB2_CLK_RST_CTRL,
		enable, 1<<0);
	} else {
#ifdef CONFIG_FPGA
		void __iomem *addrtest = ioremap(0x31500000, 4*1024);

		fh_pmu_set_reg_m(REG_USB3_CLK_RST_CTRL,
		enable, 1<<0);

		writel(5, addrtest + 0x44);
		writel(4, addrtest + 0x44);
		udelay(1000);
		writel(5, addrtest + 0x44);
		iounmap(addrtest);
#else
		fh_pmu_set_reg_m(REG_USB3_CLK_RST_CTRL,
		enable, 1<<0);
		udelay(1000);
		fh_pmu_set_reg_m(REG_USB3_CLK_RST_CTRL,
		0, 1<<0);
#endif
	}
}
EXPORT_SYMBOL(fh_pmu_usb_axi_clk_en);

void fh_pmu_sdc_reset(int slot_id)
{
}
EXPORT_SYMBOL_GPL(fh_pmu_sdc_reset);

void fh_pmu_restart(void)
{
	fh_pmu_set_reg(REG_WR_PROTECT, 0x5a5a5a5a);
	fh_pmu_set_reg(REG_GLB_RESET, SW_GLB_RST);
}

int fh_pmu_init(void)
{

	spin_lock_init(&scu_lock);

	return 0;
}
